﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Fdb;
using Fdb.Model;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Mapper.Controller
{
    [Route("api/[controller]")]
    [ApiController]
    public class MapperController : ControllerBase
    {
        private readonly IHostingEnvironment _hostingEnvironment;
        public MapperController(IHostingEnvironment hostingEnvironment)
        {
            _hostingEnvironment = hostingEnvironment;
        }

        [HttpPost]
        [Route("QueryTBL")]
        public IActionResult QueryTBL([FromBody] dynamic data)
        {
            string key = data.KEY == null ? string.Empty : data.KEY;
            string type = data.TYPE == null ? string.Empty : data.TYPE;
            if (string.IsNullOrEmpty(type))
            {
                type = "1";
            }

            try
            {
                DbInfo dbInfo = new DbInfo();
                dbInfo = this.GetCurrentDb();
                if (dbInfo == null)
                {
                    return Ok(new { success = false, message = "请先定义当前数据库！" });
                }

                DataClassMapper dataClassMapper = new DataClassMapper(dbInfo);
                if (!string.IsNullOrEmpty(key))
                {
                    dataClassMapper.TableList = dataClassMapper.TableList.Where(p => p.TableName.ToLower().Contains(key.ToLower())).ToList();
                }

                dataClassMapper.TableList = dataClassMapper.TableList.OrderBy(p => p.TableName).ToList();
                dynamic result;
                string classCode = string.Empty;
                
                if ("1".Equals(type))
                {
                    //生成数据库上下文类代码
                    string contextTbls = data.TBLS == null ? string.Empty : data.TBLS;
                    result=MapperDB(contextTbls);
                }
                else
                {
                    //生成数据表实体类代码
                    string tblname = data.TBLS == null ? string.Empty : data.TBLS;
                    result=MapperTBL(tblname);
                }

                if (result.success)
                {
                    classCode = result.data;
                }
                else
                {
                    classCode = result.message;
                }

                return Ok(new { success = true, message = "成功", data = dataClassMapper.TableList,obj = classCode });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, message = "发生异常：" + ex.ToString() });
            }
        }

        [HttpPost]
        [Route("MapperDB")]
        public IActionResult MapperDB([FromBody] dynamic data)
        {
            string contextTbls = data.TBLS == null ? string.Empty : data.TBLS;
            return Ok(MapperDB(contextTbls));
        }

        private dynamic MapperDB(string contextTbls)
        {
            try
            {
                DbInfo dbInfo = new DbInfo();
                dbInfo = this.GetCurrentDb();
                if (dbInfo == null)
                {
                    return new { success = false, message = "请先定义当前数据库！" };
                }

                DataClassMapper dataClassMapper = new DataClassMapper(dbInfo);
                dataClassMapper.ContextTbls = contextTbls;
                string classCode = dataClassMapper.CreateDbContextClass();
                classCode = classCode.Replace("<", "&lt;").Replace(">", "&gt;");
                return new { success = true, message = "成功", data = classCode };
            }
            catch (Exception ex)
            {
                return new { success = false, message = "发生异常：" + ex.ToString() };
            }
        }

        public string GetDbConfigPath()
        {
            string path = _hostingEnvironment.WebRootPath + "\\data\\dbconfig.xml";
            return path;
        }

        /// <summary>
        /// 加载数据库配置列表
        /// </summary>
        protected DbInfo GetCurrentDb()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(GetDbConfigPath());
            XmlNode xn = doc.SelectSingleNode("dbstore");
            DbInfo dbInfo = new DbInfo();
            // 得到根节点的所有子节点
            XmlNodeList xnl = xn.ChildNodes;
            foreach (XmlNode xn1 in xnl)
            {
                XmlElement xe = (XmlElement)xn1;
                XmlNodeList _xnl = xe.ChildNodes;
                if (_xnl.Item(6).InnerText == "1")
                {
                    dbInfo.ID = xn1.Attributes["id"].Value;
                    dbInfo.DatabaseType = _xnl.Item(0).InnerText;
                    dbInfo.IP = _xnl.Item(1).InnerText;
                    dbInfo.DatabaseName = _xnl.Item(2).InnerText;
                    dbInfo.UserName = _xnl.Item(3).InnerText;
                    dbInfo.Password = _xnl.Item(4).InnerText;
                    dbInfo.Note = _xnl.Item(5).InnerText;
                    dbInfo.IsCurrentDb = _xnl.Item(6).InnerText;
                    return dbInfo;
                }
            }

            return null;
        }

        [HttpPost]
        [Route("MapperTBL")]
        public IActionResult MapperTBL([FromBody] dynamic data)
        {
            string tblname = data.TBLS == null ? string.Empty : data.TBLS;
            return Ok(MapperTBL(tblname));
        }

        private dynamic MapperTBL(string tblname)
        {
            try
            {
                if (string.IsNullOrEmpty(tblname))
                {
                    return new { success = false, message = "表名空！" };
                }

                DbInfo dbInfo = new DbInfo();
                dbInfo = this.GetCurrentDb();
                if (dbInfo == null)
                {
                    return new { success = false, message = "请先定义当前数据库！" };
                }

                DataClassMapper dataClassMapper = new DataClassMapper(dbInfo);
                string classCode = dataClassMapper.CreateTableClass(tblname);
                classCode = classCode.Replace("<", "&lt;").Replace(">", "&gt;");
                return new { success = true, message = "成功", data = classCode };
            }
            catch (Exception ex)
            {
                return new { success = false, message = "发生异常：" + ex.ToString() };
            }
        }

        [HttpPost]
        [Route("QueryCfgDB")]
        public IActionResult QueryCfgDB([FromBody] dynamic data)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(GetDbConfigPath());
                XmlNode xn = doc.SelectSingleNode("dbstore");
                List<DbInfo> dbList = new List<DbInfo>();
                // 得到根节点的所有子节点
                XmlNodeList xnl = xn.ChildNodes;
                foreach (XmlNode xn1 in xnl)
                {
                    XmlElement xe = (XmlElement)xn1;
                    XmlNodeList _xnl = xe.ChildNodes;
                    DbInfo dbInfo = new DbInfo();
                    dbInfo.ID = xn1.Attributes["id"].Value;
                    dbInfo.DatabaseType = _xnl.Item(0).InnerText;
                    dbInfo.IP = _xnl.Item(1).InnerText;
                    dbInfo.DatabaseName = _xnl.Item(2).InnerText;
                    dbInfo.UserName = _xnl.Item(3).InnerText;
                    dbInfo.Password = _xnl.Item(4).InnerText;
                    dbInfo.Note = _xnl.Item(5).InnerText;
                    dbInfo.IsCurrentDb = _xnl.Item(6).InnerText;
                    dbList.Add(dbInfo);
                }

                return Ok(new { success = true, message = "成功", data = dbList.OrderByDescending(p=>p.IsCurrentDb).ToList() });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, message = "发生异常：" + ex.ToString() });
            }
        }

        [HttpPost]
        [Route("AddCfgDB")]
        public IActionResult AddCfgDB([FromBody] dynamic data)
        {
            try
            {
                DbInfo dbInfo = new DbInfo();
                dbInfo.ID = data.ID == null ? string.Empty : data.ID;
                dbInfo.IP = data.IP == null ? string.Empty : data.IP;
                dbInfo.DatabaseType = data.DatabaseType == null ? string.Empty : data.DatabaseType;
                dbInfo.DatabaseName = data.DatabaseName == null ? string.Empty : data.DatabaseName;
                dbInfo.UserName = data.UserName == null ? string.Empty : data.UserName;
                dbInfo.Password = data.Password == null ? string.Empty : data.Password;
                dbInfo.IsCurrentDb = data.IsCurrentDb == null ? string.Empty : data.IsCurrentDb;
                dbInfo.Note = data.Note == null ? string.Empty : data.Note;
                XmlDocument doc = new XmlDocument();
                doc.Load(GetDbConfigPath());
                XmlNode root = doc.SelectSingleNode("dbstore");

                if (string.IsNullOrEmpty(dbInfo.ID))
                {
                    dbInfo.ID = Guid.NewGuid().ToString();
                    XmlElement xe1 = doc.CreateElement("db");
                    xe1.SetAttribute("id", dbInfo.ID);

                    XmlElement xesub1 = doc.CreateElement("field");
                    xesub1.SetAttribute("id", "DatabaseType");
                    xe1.AppendChild(xesub1);

                    XmlElement xesub11 = doc.CreateElement("value");
                    xesub11.InnerText = dbInfo.DatabaseType;
                    xesub1.AppendChild(xesub11);

                    XmlElement xesub2 = doc.CreateElement("field");
                    xesub2.SetAttribute("id", "IP");
                    xe1.AppendChild(xesub2);

                    XmlElement xesub21 = doc.CreateElement("value");
                    xesub21.InnerText = dbInfo.IP;
                    xesub2.AppendChild(xesub21);

                    XmlElement xesub3 = doc.CreateElement("field");
                    xesub3.SetAttribute("id", "DatabaseName");
                    xe1.AppendChild(xesub3);

                    XmlElement xesub31 = doc.CreateElement("value");
                    xesub31.InnerText = dbInfo.DatabaseName;
                    xesub3.AppendChild(xesub31);

                    XmlElement xesub4 = doc.CreateElement("field");
                    xesub4.SetAttribute("id", "UserName");
                    xe1.AppendChild(xesub4);

                    XmlElement xesub41 = doc.CreateElement("value");
                    xesub41.InnerText = dbInfo.UserName;
                    xesub4.AppendChild(xesub41);

                    XmlElement xesub5 = doc.CreateElement("field");
                    xesub5.SetAttribute("id", "Password");
                    xe1.AppendChild(xesub5);

                    XmlElement xesub51 = doc.CreateElement("value");
                    xesub51.InnerText = dbInfo.Password;
                    xesub5.AppendChild(xesub51);

                    XmlElement xesub6 = doc.CreateElement("field");
                    xesub6.SetAttribute("id", "Note");
                    xe1.AppendChild(xesub6);

                    XmlElement xesub61 = doc.CreateElement("value");
                    xesub61.InnerText = dbInfo.Note;
                    xesub6.AppendChild(xesub61);

                    XmlElement xesub7 = doc.CreateElement("field");
                    xesub7.SetAttribute("id", "IsCurrentDb");
                    xe1.AppendChild(xesub7);

                    XmlElement xesub71 = doc.CreateElement("value");
                    xesub71.InnerText = dbInfo.IsCurrentDb;
                    xesub7.AppendChild(xesub71);

                    root.AppendChild(xe1);
                }
                else
                {
                    XmlNodeList list = root.SelectNodes("//db");
                    foreach (XmlNode node in list)
                    {
                        string fileID = node.Attributes["id"].Value;
                        if (fileID.Equals(dbInfo.ID))
                        {
                            XmlNodeList childList = node.ChildNodes;
                            foreach (XmlNode childNode in childList)
                            {
                                string attr = childNode.Attributes["id"].Value;
                                XmlNode valNode = childNode.FirstChild;
                                string val = valNode.InnerText;
                                switch (attr)
                                {
                                    case "DatabaseType": valNode.InnerText = dbInfo.DatabaseType; break;
                                    case "IP": valNode.InnerText = dbInfo.IP; break;
                                    case "DatabaseName": valNode.InnerText = dbInfo.DatabaseName; break;
                                    case "UserName": valNode.InnerText = dbInfo.UserName; break;
                                    case "Password": valNode.InnerText = dbInfo.Password; break;
                                    case "Note": valNode.InnerText = dbInfo.Note; break;
                                    case "IsCurrentDb": valNode.InnerText = dbInfo.IsCurrentDb; break;
                                }
                            }
                        }
                    }
                }

                doc.Save(GetDbConfigPath());

                //将其余配置置为否
                if (dbInfo.IsCurrentDb == "1")
                {
                    XmlNodeList list = root.SelectNodes("//db");
                    foreach (XmlNode node in list)
                    {
                        string fileID = node.Attributes["id"].Value;
                        if (!fileID.Equals(dbInfo.ID))
                        {
                            XmlNodeList childList = node.ChildNodes;
                            foreach (XmlNode childNode in childList)
                            {
                                string attr = childNode.Attributes["id"].Value;
                                XmlNode valNode = childNode.FirstChild;
                                string val = valNode.InnerText;
                                switch (attr)
                                {
                                    case "IsCurrentDb": valNode.InnerText = "0"; break;
                                }
                            }
                        }
                    }

                    doc.Save(GetDbConfigPath());
                }

                return Ok(new { success = true, message = "成功", data = dbInfo.ID });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, message = "发生异常：" + ex.ToString() });
            }
        }

        [HttpPost]
        [Route("ViewCfgDB")]
        public IActionResult ViewCfgDB([FromBody] dynamic data)
        {
            try
            {
                string id = data.ID == null ? string.Empty : data.ID;
                if (string.IsNullOrEmpty(id))
                {
                    return Ok(new { success = false, message = "id空！" });
                }

                DbInfo dbInfo = new DbInfo();
                XmlDocument doc = new XmlDocument();
                doc.Load(GetDbConfigPath());
                XmlNode xn = doc.SelectSingleNode("dbstore");

                XmlNodeList list = xn.SelectNodes("//db");
                foreach (XmlNode node in list)
                {
                    string fileID = node.Attributes["id"].Value;
                    if (fileID.Equals(id))
                    {
                        XmlNodeList childList = node.ChildNodes;
                        foreach (XmlNode childNode in childList)
                        {
                            string attr = childNode.Attributes["id"].Value;
                            XmlNode valNode = childNode.FirstChild;
                            string val = valNode.InnerText;
                            switch (attr)
                            {
                                case "DatabaseType": dbInfo.DatabaseType = val; break;
                                case "IP": dbInfo.IP = val; break;
                                case "DatabaseName": dbInfo.DatabaseName = val; break;
                                case "UserName": dbInfo.UserName = val; break;
                                case "Password": dbInfo.Password = val; break;
                                case "Note": dbInfo.Note = val; break;
                                case "IsCurrentDb": dbInfo.IsCurrentDb = val; break;
                            }
                        }
                    }
                }

                return Ok(new { success = true, message = "成功", data = dbInfo });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, message = "发生异常：" + ex.ToString() });
            }
        }

        [HttpPost]
        [Route("DelCfgDB")]
        public IActionResult DelCfgDB([FromBody] dynamic data)
        {
            try
            {
                string id = data.ID == null ? string.Empty : data.ID;
                if (string.IsNullOrEmpty(id))
                {
                    return Ok(new { success = false, message = "id空！" });
                }

                DbInfo dbInfo = new DbInfo();
                XmlDocument doc = new XmlDocument();
                doc.Load(GetDbConfigPath());
                XmlNode xn = doc.SelectSingleNode("dbstore");

                XmlNodeList list = xn.SelectNodes("//db");
                foreach (XmlNode node in list)
                {
                    string fileID = node.Attributes["id"].Value;
                    if (fileID.Equals(id))
                    {
                        node.ParentNode.RemoveChild(node);
                        doc.Save(GetDbConfigPath());
                    }
                }

                return Ok(new { success = true, message = "成功" });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, message = "发生异常：" + ex.ToString() });
            }
        }

        [HttpPost]
        [Route("Export")]
        public IActionResult Export([FromBody] dynamic data)
        {
            try
            {
                string contextTbls = data.TBLS == null ? string.Empty : data.TBLS;

                DbInfo dbInfo = new DbInfo();
                dbInfo = this.GetCurrentDb();
                if (dbInfo == null)
                {
                    return Ok(new { success = true, message = "请先定义当前数据库！" });
                }

                string exportDir = _hostingEnvironment.WebRootPath + "\\" + "export";
                if (Directory.Exists(exportDir))
                {
                    DirectoryInfo dirInfo = new DirectoryInfo(exportDir);
                    FileInfo[] files = dirInfo.GetFiles();
                    foreach (FileInfo file in files)
                    {
                        file.Delete();
                    }
                }

                string zipname = dbInfo.DatabaseName+".zip";
                DataClassMapper dataClassMapper = new DataClassMapper(dbInfo, contextTbls);
                string classCode = dataClassMapper.CreateDbContextClass();
                WriteBeanCode(dbInfo.DatabaseName + "_DbContext.cs", exportDir, classCode, "utf-8");
                foreach (var tbl in dataClassMapper.TableList)
                {
                    classCode = dataClassMapper.CreateTableClass(tbl.TableName);
                    WriteBeanCode(tbl.TableName + ".cs", exportDir, classCode, "utf-8");
                }

                string folderToZip = _hostingEnvironment.WebRootPath + "\\" + "export";
                string zipedFile = _hostingEnvironment.WebRootPath + "\\zip\\" + zipname;
                if (!Directory.Exists(_hostingEnvironment.WebRootPath + "\\zip"))
                {
                    Directory.CreateDirectory(_hostingEnvironment.WebRootPath + "\\zip");
                }

                bool result = ZipDirectory(exportDir, zipedFile);
                if (!result)
                {
                    return Ok(new { success = false, message = "压缩为zip文件出错！" });
                }

                return Ok(new { success = true, message = "成功！",data="/zip/"+ zipname });
            }
            catch (Exception ex)
            {
                return Ok(new { success = true, message = "发生异常：" + ex.ToString() });
            }
        }

        /// <summary>
        /// 将实体代码写入文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// /// <param name="toDir">文件夹</param>
        /// <param name="content">文件内容</param>
        /// <param name="encoding">文件编码</param>
        /// <returns>写入文件的全路径</returns>
        public string WriteBeanCode(string fileName, string toDir, string content, string encoding)
        {
            if (!Directory.Exists(toDir))
            {
                Directory.CreateDirectory(toDir);
            }

            string path = toDir + "\\" + fileName;
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
            byte[] data = Encoding.GetEncoding(encoding).GetBytes(content);
            fs.Write(data, 0, data.Length);
            fs.Flush();
            fs.Close();

            return path;
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="folderToZip">要压缩的文件夹路径</param>
        /// <param name="zipedFile">压缩文件完整路径</param>
        /// <returns>是否压缩成功</returns>
        public bool ZipDirectory(string folderToZip, string zipedFile)
        {
            bool result = ZipDirectory(folderToZip, zipedFile, null);
            return result;
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="folderToZip">要压缩的文件夹路径</param>
        /// <param name="zipedFile">压缩文件完整路径</param>
        /// <param name="password">密码</param>
        /// <returns>是否压缩成功</returns>
        public bool ZipDirectory(string folderToZip, string zipedFile, string password)
        {
            bool result = false;
            if (!Directory.Exists(folderToZip))
                return result;

            ZipOutputStream zipStream = new ZipOutputStream(System.IO.File.Create(zipedFile));
            zipStream.SetLevel(6);
            if (!string.IsNullOrEmpty(password)) zipStream.Password = password;

            result = ZipDirectory(folderToZip, zipStream, "");

            zipStream.Finish();
            zipStream.Close();

            return result;
        }

        /// <summary>
        /// 递归压缩文件夹的内部方法
        /// </summary>
        /// <param name="folderToZip">要压缩的文件夹路径</param>
        /// <param name="zipStream">压缩输出流</param>
        /// <param name="parentFolderName">此文件夹的上级文件夹</param>
        /// <returns></returns>
        private bool ZipDirectory(string folderToZip, ZipOutputStream zipStream, string parentFolderName)
        {
            bool result = true;
            string[] folders, files;
            ZipEntry ent = null;
            FileStream fs = null;
            Crc32 crc = new Crc32();

            try
            {
                ent = new ZipEntry(Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/"));
                zipStream.PutNextEntry(ent);
                zipStream.Flush();

                files = Directory.GetFiles(folderToZip);
                foreach (string file in files)
                {
                    fs = System.IO.File.OpenRead(file);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    ent = new ZipEntry(Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/" + Path.GetFileName(file)));
                    ent.DateTime = DateTime.Now;
                    ent.Size = fs.Length;

                    fs.Close();

                    crc.Reset();
                    crc.Update(buffer);

                    ent.Crc = crc.Value;
                    zipStream.PutNextEntry(ent);
                    zipStream.Write(buffer, 0, buffer.Length);
                }

            }
            catch
            {
                result = false;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                    fs.Dispose();
                }
                if (ent != null)
                {
                    ent = null;
                }
                GC.Collect();
                GC.Collect(1);
            }

            folders = Directory.GetDirectories(folderToZip);
            foreach (string folder in folders)
                if (!ZipDirectory(folder, zipStream, folderToZip))
                    return false;

            return result;
        }
    }
}